/*
 * Created on 04.02.2004
 *
 * To change the template for this generated file go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
package org.jchains.intercept;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import com.sun.org.apache.bcel.internal.*;
import com.sun.org.apache.bcel.internal.generic.*;
import com.sun.org.apache.bcel.internal.classfile.*;
//import com.sun.org.apache.bcel.internal.util.*;
import com.sun.org.apache.bcel.internal.util.ClassLoader;


/**
 * @author Marc
 *
 * To change the template for this generated type comment go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
public class ExchangingClassLoader extends URLClassLoader {
	static int number;
	ClassLoader parent;
	File f;
	PrintWriter pw;
	ZipFile zf;
	InputStream zis;

	/**
	 * 
	 */

	public ExchangingClassLoader(URL[] u) {

		super(u);
		try {
			zf = new ZipFile(u[0].getFile());
			System.out.println("*" + zf.getName() + "*");
		} catch (Exception e) {
			throw new InternalError(e + ":" + u[0].getFile());
		}
		initPrinter();
	}

	//	public ExchangingClassLoader() {
	//		super();
	//		initPrinter();
	//		// TODO Auto-generated constructor stub
	//	}

	/**
	 * @param deferTo
	 */
	//	public ExchangingClassLoader(java.lang.ClassLoader deferTo) {
	//		super(deferTo);
	//		initPrinter();
	//		// TODO Auto-generated constructor stub
	//	}

	/**
	 * @param ignored_packages
	 */
	//	public ExchangingClassLoader(String[] ignored_packages) {
	//		super(ignored_packages);
	//		initPrinter();
	//		// TODO Auto-generated constructor stub
	//	}

	/**
	 * @param deferTo
	 * @param ignored_packages
	 */
	//	public ExchangingClassLoader(
	//		java.lang.ClassLoader deferTo,
	//		String[] ignored_packages) {
	//		super(deferTo, ignored_packages);
	//		initPrinter();
	//		// TODO Auto-generated constructor stub
	//	}

	/**
	 * @param urls
	 * @param parent
	 * @param factory
	 */

	private void initPrinter() throws InternalError {
		do {

			f = new File("c:/temp/classes.txt." + number++);
		} while (f.exists());

		try {
			pw = new PrintWriter(new FileOutputStream(f), true);

		} catch (Exception e) {
			throw new InternalError(e + "file not writable");
		}
	}

	/* (non-Javadoc)
	 * @see java.lang.ClassLoader#loadClass(java.lang.String, boolean)
	 */

	/* (non-Javadoc)
	 * @see java.lang.ClassLoader#loadClass(java.lang.String)
	 */

	static char[] crlf = new char[] { 13 };
	static Class<JChainsSecInterceptor> z =
		org.jchains.intercept.JChainsSecInterceptor.class;

	public Class loadClass(String name) throws ClassNotFoundException {
		// TODO Auto-generated method stub
		if (name != null) {

			pw.write(name);
			pw.write('-');
			// System.out.println(name);
			if (name.equals("com.sun.javaws.security.JavaWebStartSecurity")) {

				try {
					// name = "org.illegalaccess.jchains.intercept.JChainsSecInterceptor";
					//return z; 
					//name = "org.illegalaccess.jchains.intercept.JChainsSecInterceptor";
					//name = name + ".class";
					String fname = name.replace('.', '/');
					String fname2 = name.replace('.', '\\');
					ZipEntry ze = zf.getEntry(fname + ".class");
					if (ze == null)
						ze = zf.getEntry(fname2 + ".class");
					//			log.info(ze.getName());
					System.out.println("*" + ze);
					System.out.println("*" + fname);
					System.out.println("*" + fname2);
					zis = zf.getInputStream(ze);

					ClassParser cp = new ClassParser(zis, ze.getName());
					JavaClass jv = cp.parse();

					ClassGen modClass = new ClassGen(jv);

					ConstantPoolGen cpg = modClass.getConstantPool();
					ObjectType sm =
						(ObjectType) BCELHelper.getType(SecurityManager.class);
					//"Ljava/security/Permission;"

					System.out.println(sm);

					// add the field 

					FieldGen field;

					InstructionFactory _factory = new InstructionFactory(cpg);

					field =
						new FieldGen(
							Constants.ACC_STATIC,
							new ObjectType("java.lang.SecurityManager"),
							"sm",
							cpg);
							
					modClass.addField(field.getField());

					// method1 
					InstructionList il = new InstructionList();

					MethodGen method =
						new MethodGen(
							Constants.ACC_PUBLIC,
							Type.VOID,
							new Type[] {
								 new ObjectType("java.security.Permission")},
							new String[] { "arg0" },
							"checkPermission",
							name,
							il,
							cpg);

//					InstructionHandle ih_0 =
						il.append(
							_factory.createFieldAccess(
								name,
								"sm",
								new ObjectType("java.lang.SecurityManager"),
								Constants.GETSTATIC));

					BranchInstruction ifnull_3 =
						InstructionFactory.createBranchInstruction(
							Constants.IFNULL,
							null);
					il.append(ifnull_3);
				//	InstructionHandle ih_6 =
						il.append(
							_factory.createFieldAccess(
								name,
								"sm",
								new ObjectType("java.lang.SecurityManager"),
								Constants.GETSTATIC));

					il.append(InstructionFactory.createLoad(Type.OBJECT, 1));
					il.append(
						_factory.createInvoke(
							"java.lang.SecurityManager",
							"checkPermission",
							Type.VOID,
							new Type[] {
								 new ObjectType("java.security.Permission")},
							Constants.INVOKEVIRTUAL));
					InstructionHandle ih_13 =
						il.append(InstructionFactory.createReturn(Type.VOID));
					ifnull_3.setTarget(ih_13);
					method.setMaxStack();
					method.setMaxLocals();
					modClass.addMethod(method.getMethod());
					il.dispose();

					//				FieldGen currentSecurityManager = new FieldGen(Constants.ACC_PUBLIC,sm,"currentSecurityManager",cpg);
					//				Field fcsm = currentSecurityManager .getField(); 
					//				modClass.addField(fcsm);
					//			
					//												
					//				int getSecurityManager= cpg.addMethodref("java.lang.System","getSecurityManager","()Ljava/lang/SecurityManager;");
					//				
					//				int invokeCheckPermission = cpg.addMethodref("java.lang.SecurityManager","checkPermission","(Ljava/security/Permission;)V");
					//				
					//				InstructionList il = new InstructionList(); 
					//				InstructionList il1 = new InstructionList(); 
					//				
					//				InstructionFactory ifact = new InstructionFactory (cpg);
					//    				
					//				org.apache.bcel.generic.ObjectType ot =  new org.apache.bcel.generic.ObjectType("java.security.Permission"); //"Ljava/security/Permission;"
					//				
					//				MethodGen checkit = new MethodGen(Constants.ACC_PUBLIC,Type.VOID,new Type[]{ot},new String[]{"permission"},"checkPermission",name,il,cpg);
					//					
					//				il.append(ifact.createGetStatic(name,"currentSecurityManager",BCELHelper.getType(SecurityManager.class)));
					//				il.append(new INVOKEVIRTUAL(invokeCheckPermission));
					//				il.append(new RETURN());
					//                  
					//                checkit.setMaxLocals(); 
					//                checkit.setMaxStack(); 
					//
					//				modClass.addMethod(checkit.getMethod());
					//				
					//				MethodGen staticinit = new MethodGen(Constants.ACC_PUBLIC|Constants.ACC_STATIC,Type.VOID,new Type[]{},new String[]{},Constants.STATIC_INITIALIZER_NAME,name,il1,cpg);
					//                
					//				il1.append(new INVOKESTATIC(getSecurityManager));
					//				il1.append(ifact.createPutStatic(name,"currentSecurityManager",BCELHelper.getType(SecurityManager.class)));
					//				il1.append(new RETURN());
					//    
					//                staticinit.setMaxLocals();
					//				staticinit.setMaxStack(); 
					//					            
					//				modClass.addMethod(staticinit.getMethod());

					InstructionList il2 = new InstructionList();
					MethodGen methodinit =
						new MethodGen(
							Constants.ACC_STATIC,
							Type.VOID,
							Type.NO_ARGS,
							new String[] {
					}, "<clinit>", name, il2, cpg);

					il2.append(
						_factory.createInvoke(
							"java.lang.System",
							"getSecurityManager",
							new ObjectType("java.lang.SecurityManager"),
							Type.NO_ARGS,
							Constants.INVOKESTATIC));
					il2.append(
						_factory.createFieldAccess(
							name,
							"sm",
							new ObjectType("java.lang.SecurityManager"),
							Constants.PUTSTATIC));
					il2.append(InstructionFactory.createReturn(Type.VOID));
					methodinit.setMaxStack();
					methodinit.setMaxLocals();
					modClass.addMethod(methodinit.getMethod());
					il2.dispose();

					JavaClass newClass = modClass.getJavaClass();
					newClass.dump(new File("JavaWebStartSecurity_neu.class"));
					ByteArrayOutputStream bos = new ByteArrayOutputStream();
					newClass.dump(bos);
					bos.flush();
					byte[] newarray = bos.toByteArray();

					return defineClass(
						"com.sun.javaws.security.JavaWebStartSecurity",
						newarray,
						0,
						newarray.length);
				} catch (ClassCastException e) {
					System.out.println(e.getMessage());
					e.printStackTrace();
					throw new InternalError(e.getMessage());
				} catch (Exception e) {
					e.printStackTrace();
					throw new InternalError(name + " not in " + zf.getName());
				}
			}
			pw.write(name);
			pw.write(crlf);
			pw.flush();

		}
		return super.loadClass(name);
	}

	/*	protected synchronized Class loadClass(String name, boolean resolve)
			throws ClassNotFoundException {
			// TODO Auto-generated method stub
			System.out.println(name);
			return parent.loadClass(name);
		}*/

	/* (non-Javadoc)
	 * @see java.lang.ClassLoader#loadClass(java.lang.String, boolean)
	 */

	protected synchronized Class loadClass(String name, boolean resolve)
		throws ClassNotFoundException {
		pw.write("with resolve:" + name + ":" + resolve);
		// TODO Auto-generated method stub
		return super.loadClass(name, resolve);
	}

	/* (non-Javadoc)
	 * @see java.lang.ClassLoader#findClass(java.lang.String)
	 */
	protected Class findClass(String name) throws ClassNotFoundException {
		// TODO Auto-generated method stub
		pw.write("find:" + name);
		return super.findClass(name);
	}

	public static void main(String[] args) {
		try {

			ExchangingClassLoader ecl =
				new ExchangingClassLoader(new URL[] { new URL(args[0])});
			System.out.println("jar:" + args[0]);
			Class f = Class.forName(args[1], true, ecl);
			Method[] mth = f.getDeclaredMethods();
			Method main = null;
			for (int i = 0; i < mth.length; i++) {
				System.out.println(mth[i]);
				if (mth[i].getName().equals("main")) {
					main = mth[i];
					break;
				}
			}
			String[] arrs = new String[args.length - 2];
			System.arraycopy(args, 2, arrs, 0, args.length - 2);
			main.invoke(null, new Object[] { arrs });
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}
